/* ----------------------------------------------------------------------------
* File: module_talk_impl.cpp
*
* Desc: Talk module implementation
*
* Created: 21/05/2011, by Alexey Korshunov
* ---------------------------------------------------------------------------- */
#include "module_talk_impl.h"

#define MODULE_TALK_OK        0
#define MODULE_TALK_ERROR     -1


namespace ModuleManagement
{
	std::auto_ptr<TalkModule> CreateTalkModule(const TalkModuleParams& params)
	{
		std::auto_ptr<TalkModule> module;
		try
		{
			std::auto_ptr<TalkModuleImpl> obj(new TalkModuleImpl());
			obj->Init(params);
			module = obj;
		}
		catch(Common::Exception)
		{
		}

		return module;
	}
}


namespace ModuleManagement
{
	void CallbackNewInputData(void* param)
	{
		TalkModuleImpl* module = reinterpret_cast<TalkModuleImpl*>(param);
		if (module)
			SetEvent(module->NewDataEvent);
	}
}


namespace ModuleManagement
{
	TalkModuleImpl::TalkModuleImpl()
		: InputQueue(NULL)
		, OutputQueue(NULL)
		, Thread(NULL)
		, StartEvent(NULL)
		, NewDataEvent(NULL)
		, StopEvent(NULL)
	{

	}

	TalkModuleImpl::~TalkModuleImpl()
	{
		StopThread();
		DestroySyncObjects();
	}

	void TalkModuleImpl::Init(const TalkModuleParams& params)
		throw(Common::Exception)
	{
		if (params.in_queue == NULL)
			throw Common::Exception("Invalid in_stream parameter");
		if (params.out_queue == NULL)
			throw Common::Exception("Invalid out_stream parameter");

		InputQueue = params.in_queue;
		OutputQueue = params.out_queue;
	}

	Common::Error TalkModuleImpl::Start(void* param, size_t size)
	{
		// create thread
		DWORD dwThreadId;
		Thread = CreateThread(NULL, 0, &ThreadFunc, (LPVOID)this, 0, &dwThreadId);
		if (Thread == NULL)
			return Common::MODULE_ERROR;

		// wait start process
		WaitForSingleObject(StartEvent, INFINITE);
		ResetEvent(StartEvent);

		return Common::MODULE_SUCCESS;
	}

	Common::Error TalkModuleImpl::Stop(void* param, size_t size)
	{
		StopThread();
		return Common::MODULE_SUCCESS;
	}

	DWORD WINAPI TalkModuleImpl::ThreadFunc(LPVOID param)
	{
		TalkModuleImpl* module = reinterpret_cast<TalkModuleImpl*>(param);
		if (module == NULL)
			ExitThread(EXIT_FAILURE);

		// signal about start thread
		SetEvent(module->StartEvent);

		HANDLE handles[2];
		handles[0] = module->NewDataEvent;
		handles[1] = module->StopEvent;

		while(1)
		{
			DWORD dwEvent = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
			switch(dwEvent)
		    {
			case WAIT_OBJECT_0: // NewDataEvent
				{
					ResetEvent(module->NewDataEvent);

					std::string OutputMsg;
					while(module->InputQueue->GetCount())
					{
						// get input message from queue
						std::string msg;
						module->InputQueue->GetMsg(msg);

						// generate output message
						if (module->AILogic(msg, OutputMsg) != MODULE_TALK_OK)
							ExitThread(EXIT_FAILURE);

						// send output message to output queue
						module->OutputQueue->PutMsg(OutputMsg);
					}
				}
				break;
			
			case WAIT_OBJECT_0 + 1: // StopEvent
				ResetEvent(module->StopEvent);
				ExitThread(EXIT_SUCCESS);
				break;
			}
		}

		ExitThread(EXIT_SUCCESS);
	}

	void TalkModuleImpl::CreateSyncObjects() throw(Common::Exception)
	{
		StartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (StartEvent == NULL)
		  throw Common::Exception("EXCEPTION - Unable to create event object");

		NewDataEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (NewDataEvent == NULL)
		  throw Common::Exception("EXCEPTION - Unable to create event object");

		StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (StopEvent == NULL)
		  throw Common::Exception("EXCEPTION - Unable to create event object");
	}

	void TalkModuleImpl::DestroySyncObjects()
	{
		CloseHandle(StartEvent);
		CloseHandle(NewDataEvent);
		CloseHandle(StopEvent);
	}

	void TalkModuleImpl::StopThread()
	{
		SetEvent(StopEvent);
		WaitForSingleObject(Thread, INFINITE);
		CloseHandle(Thread);
	}

	int TalkModuleImpl::AILogic(const std::string& InputMsg, std::string& OutputMsg)
	{
		// TODO: Need to implement correctly
		OutputMsg = InputMsg;
		return MODULE_TALK_OK;
	}
}